package com.talkyun.openx.server;

import com.talkyun.utils.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author kevin
 */
public abstract class AbstractBaseFilter implements Filter {
    protected Logger logger = LoggerFactory.getLogger(this.getClass());
    protected final String PATH_SPLIT_REGX = "[;, ，；]";
    protected AntPathMatcher matcher = new AntPathMatcher();

    private String[] excludePath;
    private String[] includePath;

    // 可以做URLs匹配，规则如下
    //
    //  ？匹配一个字符
    //  *匹配0个或多个字符
    //  **匹配0个或多个目录

    // 用例如下
    //
    ///  /trip/api/*x    匹配 /trip/api/x，/trip/api/ax，/trip/api/abx ；但不匹配 /trip/abc/x；
    ///  /trip/a/a?x     匹配 /trip/a/abx；但不匹配 /trip/a/ax，/trip/a/abc
    ///  /**/api/alie    匹配 /trip/api/alie，/trip/dax/api/alie；但不匹配 /trip/a/api
    //   /**/*.html     匹配所有以.html结尾的路径

    @Override
    public void init(FilterConfig fc) throws ServletException {
        // init exclude path array
        excludePath = getParamToArray(getInitParameter(fc, "excludeURI", "secretExcludeURI"));
        includePath = getParamToArray(getInitParameter(fc, "includeURI", "secretIncludeURI"));
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain fc) throws IOException, ServletException {
        String uri = ((HttpServletRequest) req).getRequestURI().toLowerCase();
        // fixed ignore .ico
        if (uri.endsWith(".ico")) {
            fc.doFilter(req, resp);
            return;
        }

        // exclude check
        if (this.isExcludeURI(uri)) {
            fc.doFilter(req, resp);
            return;
        }

        String address = getRemoteAddress((HttpServletRequest) req);
        if (this.isExcludeAddress(address)) {
            String domain = getRootDomain(((HttpServletRequest) req).getRequestURL().toString());
            if (!domain.contains("nx4k.com")) {
                fc.doFilter(req, resp);
                return;
            }
        }

        this.handle((HttpServletRequest) req, (HttpServletResponse) resp, fc);
    }

    @Override
    public void destroy() {

    }

    protected abstract void handle(HttpServletRequest req, HttpServletResponse resp, FilterChain fc) throws IOException, ServletException;

    protected boolean isExcludeAddress(String address) {
        return false;
    }

    protected boolean isExcludeURI(String uri) {
        // include 和 exclude 冲突，include为准
        for (String include : includePath) {
            if (this.isMatch(include, uri)) {
                return false;
            }
        }

        for (String exclude : excludePath) {
            if (this.isMatch(exclude, uri)) {
                return true;
            }
        }

        return false;
    }

    private String[] getParamToArray(String conf) {
        if (isBlank(conf)) {
            return new String[0];
        } else {
            List<String> list = new ArrayList<String>();
            String[] bs = conf.trim().split(PATH_SPLIT_REGX);
            for (String b : bs) {
                if (!isBlank(b)) {
                    String path = b.trim().toLowerCase();
                    list.add(path.startsWith("/") ? path : "/" + path);
                }
            }
            return list.toArray(new String[0]);
        }
    }

    protected String getRemoteAddress(HttpServletRequest req) {
        String ip = req.getHeader("X-Forwarded-For");
        if (!this.isBlank(ip)) {
            int pos = ip.indexOf(",");
            return pos != -1 ? ip.substring(0, pos).trim() : ip.trim();
        } else {
            String address = req.getRemoteAddr();
            return "0:0:0:0:0:0:0:1".equals(address) ? "127.0.0.1" : address;
        }
    }

    protected String getInitParameter(FilterConfig fc, String servletParamName, String contextParamName) {
        String param = fc.getInitParameter(servletParamName);
        if (this.isBlank(param)) {
            ServletContext sc = fc.getServletContext();
            if (sc != null) {
                param = (contextParamName == null) ? null : sc.getInitParameter(contextParamName);
            }
        }
        return isBlank(param) ? null : param.trim();
    }

    protected String getSystemProperties(String key) {
        return MapUtils.getString(System.getProperties(), key);
    }

    protected String getSystemEnvironment(String key) {
        return MapUtils.getString(System.getenv(), key, key.replace(".", "-"));
    }

    protected boolean isMatch(String pattern, String uri) {
        if (pattern.contains("*") || pattern.contains("?")) {
            return matcher.match(pattern, uri);
        }
        return uri.equalsIgnoreCase(pattern);
    }

    protected boolean isBlank(String s) {
        return s == null || s.trim().length() == 0;
    }

    protected boolean hasBlank(Object... o) {
        for (Object s : o) {
            if (s == null) {
                return true;
            }
            if (s instanceof String && isBlank((String) s)) {
                return true;
            }
        }

        return false;
    }

    protected String toString(Map<String, String> map, String split) {
        StringBuilder sb = new StringBuilder();

        for (Map.Entry<String, String> entry : map.entrySet()) {
            String key = entry.getKey();
            String val = entry.getValue();
            if (hasBlank(key, val)) {
                continue;
            }
            if (sb.length() != 0) {
                sb.append(split);
            }
            sb.append(key).append("=").append(val);
        }
        return sb.toString();
    }

    protected String getRootDomain(String url) throws MalformedURLException {
        URL biz = new URL(url);
        String host = biz.getHost();

        int pos = host.indexOf(".");
        if (pos != -1) {
            return host.substring(pos + 1);
        }
        return host;
    }

    protected String getDomainName(String url) throws MalformedURLException {
        return new URL(url).getHost();
    }

    protected void doWrite(int code, String msg, HttpServletResponse resp) {
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/plain");
        resp.setHeader("Cache-Control", "no-cache");

        resp.setStatus(code);
        try {
            resp.getWriter().write(msg);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}